package org.aphelie.torg.ruler.internal.core.text.character;

import java.io.PrintWriter;

import org.aphelie.torg.ruler.core.IModel;
import org.aphelie.torg.ruler.core.character.ICharacterAttribute;
import org.aphelie.torg.ruler.core.character.ICharacterElement;
import org.aphelie.torg.ruler.core.character.ICharacterExtension;
import org.aphelie.torg.ruler.internal.core.ischema.ISchema;
import org.aphelie.torg.ruler.internal.core.ischema.ISchemaElement;
import org.aphelie.torg.ruler.internal.core.text.DocumentTextNode;
import org.aphelie.torg.ruler.internal.core.text.IDocumentAttributeNode;
import org.aphelie.torg.ruler.internal.core.text.IDocumentElementNode;
import org.aphelie.torg.ruler.internal.core.text.IDocumentTextNode;
import org.eclipse.core.runtime.CoreException;


public class CharacterElementNode extends CharacterParentNode implements ICharacterElement {

    private static final long serialVersionUID = 1L;

    private transient ISchemaElement elementInfo;

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#createCopy()
     */
    public ICharacterElement createCopy() {
        return null;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#getAttribute(java.lang.String)
     */
    public ICharacterAttribute getAttribute(String name) {
        return (ICharacterAttribute) getNodeAttributesMap().get(name);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#getAttributes()
     */
    public ICharacterAttribute[] getAttributes() {
        return (ICharacterAttribute[]) getNodeAttributesMap().values().toArray(new ICharacterAttribute[getNodeAttributesMap().size()]);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#getAttributeCount()
     */
    public int getAttributeCount() {
        return getNodeAttributesMap().size();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#getText()
     */
    public String getText() {
        IDocumentTextNode node = getTextNode();
        return node == null ? "" : node.getText(); //$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#setAttribute(java.lang.String, java.lang.String)
     */
    public void setAttribute(String name, String value) throws CoreException {
        setXMLAttribute(name, value);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#setText(java.lang.String)
     */
    public void setText(String text) throws CoreException {
        IDocumentTextNode node = getTextNode();
        String oldText = node == null ? null : node.getText();
        if (node == null) {
            node = new DocumentTextNode();
            node.setEnclosingElement(this);
            addTextNode(node);
        }
        node.setText(text.trim());
        firePropertyChanged(node, P_TEXT, oldText, text);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.model.plugin.CharacterObjectNode#write()
     */
    public String write(boolean indent) {
        String sep = getLineDelimiter();
        StringBuffer buffer = new StringBuffer();
        if (indent)
            buffer.append(getIndent());

        IDocumentElementNode[] children = getChildNodes();
        String text = getText();
        buffer.append(writeShallow(false));
        if (getAttributeCount() > 0 || children.length > 0 || text.length() > 0)
            buffer.append(sep);
        if (children.length > 0 || text.length() > 0) {
            if (text.length() > 0) {
                buffer.append(getIndent());
                buffer.append("   "); //$NON-NLS-1$
                buffer.append(text);
                buffer.append(sep);
            }
            for (int i = 0; i < children.length; i++) {
                children[i].setLineIndent(getLineIndent() + 3);
                buffer.append(children[i].write(true));
                buffer.append(sep);
            }
        }
        if (getAttributeCount() > 0 || children.length > 0 || text.length() > 0)
            buffer.append(getIndent());

        buffer.append("</" + getXMLTagName() + ">"); //$NON-NLS-1$ //$NON-NLS-2$    
        return buffer.toString();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.model.plugin.CharacterObjectNode#writeShallow(boolean)
     */
    public String writeShallow(boolean terminate) {
        String sep = getLineDelimiter();
        StringBuffer buffer = new StringBuffer("<" + getXMLTagName()); //$NON-NLS-1$

        IDocumentAttributeNode[] attrs = getNodeAttributes();
        for (int i = 0; i < attrs.length; i++) {
            if (attrs[i].getAttributeValue().length() > 0)
                buffer.append(sep + getIndent() + "      " + attrs[i].write()); //$NON-NLS-1$
        }
        if (terminate)
            buffer.append("/"); //$NON-NLS-1$
        buffer.append(">"); //$NON-NLS-1$
        return buffer.toString();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getName()
     */
    public String getName() {
        return getXMLTagName();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterObject#setName(java.lang.String)
     */
    public void setName(String name) throws CoreException {
        setXMLTagName(name);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.core.plugin.ICharacterElement#getElementInfo()
     */
    public Object getElementInfo() {
        if (elementInfo == null) {
            IDocumentElementNode node = getParentNode();
            for (;;) {
                if (node == null || node instanceof ICharacterExtension)
                    break;
                node = node.getParentNode();
            }
            if (node != null) {
                ICharacterExtension extension = (ICharacterExtension) node;
                ISchema schema = (ISchema) extension.getSchema();
                if (schema != null) {
                    elementInfo = schema.findElement(getName());
                }
            }
        }
        return elementInfo;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.text.plugin.CharacterObjectNode#reconnect(org.eclipse.pde.core.plugin.ISharedCharacterModel, org.eclipse.pde.internal.core.ischema.ISchema, org.eclipse.pde.internal.core.text.IDocumentElementNode)
     */
    public void reconnect(IDocumentElementNode parent, IModel model) {
        super.reconnect(parent, model);
        // Transient Field:  Element Info
        // Not necessary to reconnect schema.
        // getElementInfo will retrieve the schema on demand if it is null  
        elementInfo = null;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.text.plugin.CharacterObjectNode#write(java.lang.String, java.io.PrintWriter)
     */
    public void write(String indent, PrintWriter writer) {
        // Used for text transfers for copy, cut, paste operations
        writer.write(write(true));
    }

}
